/**
 * @author Johannes Riedmann
 * @author Lukas K�ll
 * @author David Medenjak
 */

public class Map {

    /** first element of the map */
    private CollectionElement first;

    /** last element of the map */
    private CollectionElement last;

    /** number of entries */
    private int size;

    public Map() {
        this.first = null;
        this.last = null;
        size = 0;
    }

    /**
     * adds a CollectionElement to the map
     * @param key of the roboter/fabrik
     * @param value of the roboter/fabrik
     * @return true if adding was successful, else false
     */
    public boolean add(Object key, Object value) {

        CollectionElement addElement = new CollectionElement(key, value);

        if (first == null) {    // only if it's the first element to add
            first = addElement;
            last = addElement;
            first.next = null;
            first.prev = null;
            size++;
            return true;
        }

        if (get(key) != null) {     // if key is already existing
            return false;
        }

        if (first != null) {
            last.next = addElement;
            addElement.prev = last;
            addElement.next = null;
            last = addElement;
            size++;
        }

        return true;
    }

    /**
     * removes a CollectionElement from the map
     * @param key of element that should be removed
     * @return true if removing succeeded, else false
     */
    public boolean remove(Object key) {

        if (first == null) {
            return false;
        }

        CollectionElement currentElement = first;

        while (currentElement != null) {
            if (currentElement.key.equals(key)) {
                if (currentElement.next != null && currentElement.prev != null) {
                    currentElement.next.prev = currentElement.prev;
                    currentElement.prev.next = currentElement.next;
                } else if (currentElement.next != null
                        && currentElement.prev == null) {
                    // we delete the first element of the map
                    first = currentElement.next;
                } else if (currentElement.next == null
                        && currentElement.prev != null) {
                    // we delete the last element of the map
                    last = currentElement.prev;
                }

                currentElement = null;
                size--;

                return true;
            }
            currentElement = currentElement.next;
        }

        return false;
    }

    /**
     * returns a roboter/fabrik by its key
     * @param key of the roboter/fabrik
     * @return Object-representation of the roboter/fabrik
     */
    public Object get(Object key) {
        if (first == null) {
            return null;
        } else if (first.key.equals(key)) {
            return first.value;
        }

        CollectionElement currentElement = first;

        while (currentElement != null) {
            if (currentElement.key.equals(key)) {
                return currentElement.value;
            }
            currentElement = currentElement.next;
        }

        return null;
    }

    /**
     * @return size of the map
     */
    public int getSize() {
        return size;
    }

    /**
     * @return a new iterator for the map
     */
    public Iter iterator() {
        return new Iterator();
    }

    private class Iterator implements Iter {

        /** current element the iterator is pointing on */
        CollectionElement currentElement;

        /** is first element? */
        private boolean isFirstElement;

        public Iterator() {
            this.currentElement = first;
            isFirstElement = true;
        }

        @Override
        public Object next() {
            if (isFirstElement) {
                isFirstElement = false;
                return currentElement.value;
            }
            if (!hasNext()) {
                return null;
            } else {
                currentElement = currentElement.next;
                return currentElement.value;
            }
        }

        @Override
        public boolean hasNext() {
            if (currentElement.equals(last)) {
                return false;
            }
            return true;
        }

        @Override
        public Object previous() {
            if (!hasPrevious()) {
                return null;
            } else {
                currentElement = currentElement.prev;
                return currentElement.value;
            }
        }

        @Override
        public boolean hasPrevious() {
            if (currentElement.equals(first)) {
                return false;
            }
            return true;
        }

    }

    private class CollectionElement {

        /** identification for this element */
        private final Object key;

        /** content of this element */
        private Object value;

        /** reference to the next element */
        private CollectionElement next;

        /** reference to the previous element */
        private CollectionElement prev;

        public CollectionElement(Object key, Object value) {
            this.key = key;
            this.value = value;
            this.next = null;
            this.prev = null;
        }
    }

}